package ph.com.gs3.formalistics.services;

import java.util.ArrayList;
import java.util.List;

import ph.com.gs3.formalistics.communicators.Communicator;
import ph.com.gs3.formalistics.communicators.DocumentCommunicatorModule;
import ph.com.gs3.formalistics.communicators.FormCommunicatorModule;
import ph.com.gs3.formalistics.managers.DocumentManager;
import ph.com.gs3.formalistics.managers.FormManager;
import ph.com.gs3.formalistics.model.dao.ConfigDAO;
import ph.com.gs3.formalistics.model.dao.DocumentsDAO;
import ph.com.gs3.formalistics.model.dao.FormsDAO;
import ph.com.gs3.formalistics.model.valueobjects.application.Document;
import ph.com.gs3.formalistics.model.valueobjects.application.FormStub;
import ph.com.gs3.formalistics.utilities.FLLogger;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;

/**
 * 
 * @author Ervinne Sodusta
 */
public class DocumentsUpdateService extends Service {

	public static final String TAG = DocumentsUpdateService.class.getSimpleName();

	public static final String EXTRA_SERVER = "server";

	private List<DocumentsUpdateListener> listeners;

	private IBinder binder;

	private String server;

	private Looper serviceLooper;
	private UpdateServiceHandler updateServiceHandler;

	// Managers
	private FormManager formManager;
	private DocumentManager documentManager;

	// Data Access Objects
	private ConfigDAO configDAO;
	private FormsDAO formsDAO;
	private DocumentsDAO documentsDAO;

	// Communicators
	private FormCommunicatorModule formCommModule;
	private DocumentCommunicatorModule documentCommModule;

	public DocumentsUpdateService() {
		listeners = new ArrayList<DocumentsUpdateListener>();
		binder = new DocumentsUpdateServiceBinder(this);
	}

	@Override
	public void onCreate() {
		// Start up the thread running the service. Note that we create a
		// separate thread because the service normally runs in the process's
		// main thread, which we don't want to block. We also make it
		// background priority so CPU-intensive work will not disrupt our UI.
		HandlerThread thread = new HandlerThread("ServiceStartArguments",
		        Process.THREAD_PRIORITY_BACKGROUND);
		thread.start();

		// Get the HandlerThread's Looper and use it for our Handler
		serviceLooper = thread.getLooper();
		updateServiceHandler = new UpdateServiceHandler(serviceLooper);
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {

		// get the server as passed by the caller of this service
		server = intent.getExtras().getString(EXTRA_SERVER);

		// do not start the service if there is no server given
		if (server != null) {

			FLLogger.d(TAG, "DocumentsUpdateService started at server " + server + ". Start id: "
			        + startId);

			initializeManagers();

			// For each start request, send a message to start a job and deliver the
			// start ID so we know which request we're stopping when we finish the job
			Message msg = updateServiceHandler.obtainMessage();
			msg.arg1 = startId;
			updateServiceHandler.sendMessage(msg);
		}

		// We want this service to continue running until it is explicitly
		// stopped, so return sticky.
		return START_REDELIVER_INTENT;
	}

	@Override
	public IBinder onBind(Intent intent) {
		return binder;
	}

	// ========================================================================
	// Functional Methods

	private void initializeManagers() {

		// initialize the form manager
		configDAO = new ConfigDAO(getApplicationContext());
		formsDAO = new FormsDAO(getApplicationContext());
		formCommModule = new FormCommunicatorModule(new Communicator(), server);

		formManager = new FormManager(configDAO, formsDAO, formCommModule);

		// initialize the documents manager
		documentsDAO = new DocumentsDAO(getApplicationContext());
		documentCommModule = new DocumentCommunicatorModule(new Communicator(), server);

		documentManager = new DocumentManager(documentsDAO, documentCommModule);

	}

	// ========================================================================
	// Service Handler

	private final class UpdateServiceHandler extends Handler {

		private boolean documentsUpdatedSuccessfully = false;
		private List<Document> updatedDocuments = null;
		private Document lastUpdatedDocument = null;
		private Exception onUpdateException = null;

		private int starterId;

		public UpdateServiceHandler(Looper looper) {
			super(looper);
		}

		@Override
		public void handleMessage(Message msg) {

			// get all forms available then update the documents of each forms
			// List<FormStub> formStubs = formManager.

			Handler postServiceExecuteHandler = new Handler(Looper.getMainLooper());
			postServiceExecuteHandler.post(postExecuteTask);
		}

		private Runnable postExecuteTask = new Runnable() {

			@Override
			public void run() {

			}

		};

		private Runnable onDocumentUpdatedTask = new Runnable() {

			@Override
			public void run() {

			}

		};

	}

	// ========================================================================
	// Interfaces and Notification Utility Methods

	private static interface DocumentsUpdateListener {

		public void onDocumentUpdated(Document document);

		public void onAllDocumentsUpdated();

		public void onDocumentUpdateFail(Exception e);

	}

	// ========================================================================
	// Binder

	public static class DocumentsUpdateServiceBinder extends Binder {

		private DocumentsUpdateService service;

		public DocumentsUpdateServiceBinder(DocumentsUpdateService service) {
			this.service = service;
		}

		public synchronized void registerListener(DocumentsUpdateListener listener) {
			service.listeners.add(listener);
		}

		public synchronized void removeListener(DocumentsUpdateListener listener) {
			service.listeners.remove(listener);
		}

	}

}
